#    Copyright 2012, Robert Baruch (robert.c.baruch@gmail.com)
#
#    This file is part of PYZ.
#
#    PYZ is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    PYZ is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with PYZ.  If not, see <http://www.gnu.org/licenses/>.

'''
Created on Jun 4, 2012

@author: robertbaruch
'''
import unittest
import pyz
import io

class Test(unittest.TestCase):


    def setUp(self):
        header = [0x03,                      # version
                  0x00,                      # flags1
                  0x00, 0x00,                # padding
                  0x00, 0x00,                # highmem
                  0x00, 0x50,                # initial PC
                  0x00, 0x00,                # dictionary
                  0x00, 0x00,                # object table (offset 10, 11)
                  0x00, 0x40,                # global vars
                  0x00, 0x00,                # static mem
                  0x00, 0x00, 0x00, 0x00,    # flags2
                  0x00, 0x00, 0x00, 0x00,    # ...
                  0x00, 0x00,                # abbreviations table (offset 24, 25)
                  0x00, 0x30,                # length of file (offset 26, 27)
                  0x0D, 0x68,                # checksum (offset 28, 29)
                  0x00, 0x00,                # interpreter number, version
                  0xFF, 0xFF,                # screen lines, screen characters
                  0xFF, 0xFF, 0xFF, 0xFF,    # screen width, screen height
                  0xFF, 0xFF,                # font width, font height
                  0x00, 0x00,                # routines offset
                  0x00, 0x00,                # static strings offset
                  0x00, 0x00,                # default bgrd, fgrd color
                  0x00, 0x00,                # terminating characters table
                  0x00, 0x00,                # stream 3 width in pixels
                  0x00, 0x00,                # standard revision number
                  0x00, 0x00,                # alphabet table address
                  0x00, 0x00,                # header ext table address
                  0x00, 0x00, 0x00, 0x00,    # padding to make header length 0x40
                  0x00, 0x00, 0x00, 0x00     # ...
                  ]
        globalvars = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                   0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]
        code = [0xDE, 0xAD, 0xBE, 0xEF]*4
        obj1 =     [0x80, 0x00, 0x00, 0x00,         # (object 1) attributes
                    0x00,                           # parent = 0
                    0x00,                           # sibling = 0
                    0x02,                           # child = 2
                    0x00, 0x00]                     # property table (offset 7,8)
        obj2 =     [0x00, 0x00, 0x80, 0x00,         # (object 2) attributes
                    0x01,                           # parent = 1
                    0x00,                           # sibling = 0
                    0x00,                           # child = 0
                    0x00, 0x00]                     # property table (offset 7,8)
        obj3 =     [0x00, 0xFF, 0xFF, 0x00,         # (object 3) attributes
                    0x00,                           # parent = 0
                    0x00,                           # sibling = 0
                    0x00,                           # child = 0
                    0x00, 0x00]                     # property table (offset 7,8)
                    
        default_props = ([0xAA, 0xBB, 0xCC, 0xDD] * 16)[0:62]
        name1 = pyz.strtozstr("thing1")
        proptable1 = [ len(name1)>>1 ] + list(name1)
        # dec 18: 3b 59 
        # dec 17: 00 00 
        # dec 07: 00 00 
        props1 = [0x32, 0x3B, 0x59, 0x31, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00]
        
        name2 = pyz.strtozstr("thing2")
        proptable2 = [ len(name2)>>1 ] + list(name2)
        # dec 18: 45 93 45 e0 49 91 3e d9
        # dec 17: 29 5c
        props2 = [0xF2, 0x45, 0x93, 0x45, 0xe0, 0x49, 0x91, 0x3e, 0xd9, 0x31, 0x29, 0x5C, 0x00 ]
        
        name3 = pyz.strtozstr("thing3")
        proptable3 = [ len(name3)>>1 ] + list(name3)
        # dec 30: 1A
        props3 = [0x1E, 0x1A, 0x00]
        
        abbreviation3 = [0x96, 0x60] # ", "
        abbreviation5 = [0xBB, 0x00] # "is "
        abbreviation10 = [0xD1, 0x60] # "of "
        abbreviation50 = [0xD2, 0x60] # "on "
        abbreviation68 = [0x18, 0xF4, 0xEB, 0x20] # "about "
        abbreviations_addrs = [0x00, 0x00]*3*32
        
        file = [ header, globalvars, code, 
                default_props, obj1, obj2, obj3, 
                proptable1, props1, 
                proptable2, props2,
                proptable3, props3,
                abbreviation3,
                abbreviation5,
                abbreviation10,
                abbreviation50,
                abbreviation68,
                abbreviations_addrs ]
        locs = [ 0 ]
        for f in file:
            locs.append(locs[-1] + len(f))
            
        data = []
        for f in file:
            data += f
        
        # fix up object table pointers
        data[locs[4] + 7] = locs[7] >> 8
        data[locs[4] + 8] = locs[7] & 0xFF
        data[locs[5] + 7] = locs[9] >> 8
        data[locs[5] + 8] = locs[9] & 0xFF
        data[locs[6] + 7] = locs[11] >> 8
        data[locs[6] + 8] = locs[11] & 0xFF
        
        # fix up object table location
        data[10] = locs[3] >> 8
        data[11] = locs[3] & 0xFF
        
        # fix up abbreviation address table (packed addresses)
        aloc = 18
        
        data[locs[aloc]+3*2] = (locs[13]>>1) >> 8
        data[locs[aloc]+3*2+1] = (locs[13]>>1) & 0xFF
        data[locs[aloc]+5*2] = (locs[14]>>1) >> 8
        data[locs[aloc]+5*2+1] = (locs[14]>>1) & 0xFF
        data[locs[aloc]+10*2] = (locs[15]>>1) >> 8
        data[locs[aloc]+10*2+1] = (locs[15]>>1) & 0xFF
        data[locs[aloc]+50*2] = (locs[16]>>1) >> 8
        data[locs[aloc]+50*2+1] = (locs[16]>>1) & 0xFF
        data[locs[aloc]+68*2] = (locs[17]>>1) >> 8
        data[locs[aloc]+68*2+1] = (locs[17]>>1) & 0xFF
        
        # fix up abbreviation table address
        data[24] = locs[aloc] >> 8
        data[25] = locs[aloc] & 0xFF
        
        # fix up file length
        data[26] = len(data) >> 9
        data[27] = (len(data) >> 1) & 0xFF
        
        checksum = sum(data[locs[1]:]) & 0xFFFF
        data[28] = checksum >> 8
        data[29] = checksum & 0xFF
        
        self.ostr = io.StringIO()   
        self.istr = io.StringIO()     
        self.segment_offsets = locs
        self.memory = pyz.Memory(zmem = data, ostr = self.ostr, istr = self.istr)
        self.interpreter = pyz.Instruction_Interpreter(self.memory)

    def _set_code(self, code, offset = 0):
        start = offset + self.memory.header.initial_PC
        self.memory.memory[start : start + len(code)] = code
        
    def _fixup_code_for_location(self, offset, loc):
        loc += self.memory.header.initial_PC
        code = [loc>>8, loc&0xFF]
        self._set_code(code, offset)
        
    def _fixup_code_for_packed_location(self, offset, loc):
        loc += self.memory.header.initial_PC
        loc >>= 1
        code = [loc>>8, loc&0xFF]
        self._set_code(code, offset)
        
    def _run_code(self, trace = False, local_vars = []):
        start = self.memory.header.initial_PC
        return self.interpreter.interpret(start, trace = trace, local_vars = local_vars)
    
    def _print_instr(self, offset = 0, num_instr = 1):
        loc = offset + self.memory.header.initial_PC
        while num_instr > 0:
            loc, trace = self.interpreter.interpret_instr(loc, False)
            print(trace)
            num_instr -= 1
        print("{:04X}:".format(loc))
        

    def tearDown(self):
        pass
    
    
    #
    # In general, each test depends on at least one of the tests above it
    # succeeding. However, be aware that the unit test doesn't run the
    # tests in the order they are given here.
    #

    def testRtrue(self):
        self._set_code([0xB0]) # RTRUE
        self.assertEqual(self._run_code(), 1)
        
    def testRfalse(self):
        self._set_code([0xB1]) # RFALSE
        self.assertEqual(self._run_code(), 0)
        
    def testRetWord(self):
        self._set_code([0x8B, 0xAA, 0xBB]) # RET AABB
        self.assertEqual(self._run_code(), 0xAABB)
        
    def testRetByte(self):
        self._set_code([0x9B, 0xCC]) # RET CC
        self.assertEqual(self._run_code(), 0xCC)
        
    def testRetGlobal(self):
        self._set_code([0xAB, 0x10]) # RET G00
        self.memory.set_global(0, 0xDD)
        self.assertEqual(self._run_code(), 0xDD)
        
    def testRetLocal(self):
        self._set_code([0xAB, 0x01]) # RET L00
        self.assertEqual(self._run_code(local_vars = [0xEE]), 0xEE)
        
    def testPop(self):
        self._set_code([0xE8, 0x3F, 0xFE, 0xDC, # PUSH FEDC
                        0xE8, 0x3F, 0x12, 0x34, # PUSH 1234
                        0xB9,                   # POP
                        0xAB, 0x00])            # RET STACK
        self.assertEqual(self._run_code(), 0xFEDC) 
        
    def testPushRetPop(self):
        self._set_code([0xE8, 0x3F, 0xFE, 0xDC, # PUSH FEDC
                        0xAB, 0x00])            # RET STACK
        self.assertEqual(self._run_code(), 0xFEDC) 
        
    def testPushRetPopped(self):
        self._set_code([0xE8, 0x3F, 0xAB, 0xCD, # PUSH ABCD
                        0xB8])                  # RET_POPPED
        self.assertEqual(self._run_code(), 0xABCD) 
        
    def testAddImmediate(self):
        self._set_code([0x14, 0x91, 0xA2, 0x10, # ADD 91, A2 -> G00
                        0xB0])                  # RTRUE        
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x133)
        
    def testAddGlobal(self):
        self._set_code([0x54, 0x10, 0x01, 0x10, # ADD G00, 01 -> G00
                        0xB0])                  # RTRUE        
        self.memory.set_global(0, 0xF000)
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xF001)
        self.memory.set_global(0, 0xFFFF)       # make sure overflow works
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0)
        
        self._set_code([0x34, 0x01, 0x10, 0x10, # ADD 01, G00 -> G00
                        0xB0])                  # RTRUE        
        self.memory.set_global(0, 0xF000)
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xF001)
        
    def testSub(self):
        self._set_code([0x15, 0x91, 0xA2, 0x10, # SUB 91, A2 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), -0x11 & 0xFFFF)

    def testMul(self):
        self._set_code([0x16, 0x91, 0xA2, 0x10, # MUL 91, A2 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x5BC2)

    def testDiv(self):
        self._set_code([0x17, 0xFF, 0x03, 0x10, # DIV FF, 03 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x55)

    def testMod(self):
        self._set_code([0x18, 0xFE, 0x03, 0x10, # MOD FE, 03 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 2)

    def testOr(self):
        self._set_code([0x08, 0x10, 0x03, 0x10, # OR 10, 03 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x13)

    def testAnd(self):
        self._set_code([0x09, 0x11, 0x03, 0x10, # AND 11, 03 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x01)

    def testNot(self):
        self._set_code([0x8F, 0x80, 0x03, 0x10, # NOT 8003 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x7FFC)

    def testInc(self):
        self._set_code([0xE8, 0x3F, 0xAB, 0xCD, # PUSH ABCD
                        0x95, 0x00,             # INC STACK
                        0xB8])                  # RET_POPPED
        self.assertEqual(self._run_code(), 0xABCE) 
        
    def testIncOverflow(self):
        self._set_code([0xE8, 0x3F, 0xFF, 0xFF, # PUSH FFFF
                        0x95, 0x00,             # INC STACK
                        0xB8])                  # RET_POPPED
        self.assertEqual(self._run_code(), 0) 
        
    def testDec(self):
        self._set_code([0xE8, 0x3F, 0xAB, 0xCD, # PUSH ABCD
                        0x96, 0x00,             # DEC STACK
                        0xB8])                  # RET_POPPED
        self.assertEqual(self._run_code(), 0xABCC) 
        
    def testDecOverflow(self):
        self._set_code([0xE8, 0x3F, 0x00, 0x00, # PUSH 0000
                        0x96, 0x00,             # DEC STACK
                        0xB8])                  # RET_POPPED
        self.assertEqual(self._run_code(), 0xFFFF) 
        
    def testStore(self):
        self._set_code([0x0D, 0x10, 0xB4,       # STORE B4 -> G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xB4)

    def testStoreB(self):
        cloc = self.memory.header.initial_PC
        self._set_code([0xE2, 0x57, cloc, 0x06, 0x99, # STOREB code, 6, 99
                        0xB0,                         # RTRUE
                        0x00, 0x00])
        self._run_code()
        self.assertEqual(self.memory.memory[cloc+6], 0x99)
        self.assertEqual(self.memory.memory[cloc+7], 0x00)

    def testStoreW(self):
        cloc = self.memory.header.initial_PC
        self._set_code([0xE1, 0x53, cloc, 0x04, 0x99, 0xAA, # STOREW code, 4, 99AA
                        0xB0,                               # RTRUE
                        0x00, 0x00, 0x00])
        self._run_code()
        self.assertEqual(self.memory.memory[cloc+8], 0x99)
        self.assertEqual(self.memory.memory[cloc+9], 0xAA)

    def testLoad(self):
        self.memory.set_global(0, 0xBEEF)
        self._set_code([0x9E, 0x10, 0x00,       # LOAD G00 -> STACK
                        0xB8])                  # RET_POPPED
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xBEEF)

    def testPull(self):
        self._set_code([0xE8, 0x3F, 0xAB, 0xCD, # PUSH ABCD
                        0xE9, 0x7F, 0x10,       # PULL G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xABCD)
        
    def testNop(self):
        self._set_code([0xE8, 0x3F, 0xAB, 0xCD, # PUSH ABCD
                        0xB4,                   # NOP
                        0xE9, 0x7F, 0x10,       # PULL G00
                        0xB4,                   # NOP
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xABCD)
        
    def testLoadb(self):
        cloc = self.memory.header.initial_PC
        self._set_code([0x10, cloc, 0x03, 0x10, # LOADB code, 3, G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x10)

    def testLoadw(self):
        cloc = self.memory.header.initial_PC
        self._set_code([0x0F, cloc, 0x01, 0x10, # LOADB code, 1, G00
                        0xB0])                  # RTRUE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x0110)
        
    def testJump(self):
        self._set_code([0xE8, 0x3F, 0xFE, 0xDC, # PUSH FEDC
                        0x9C, 0x05,             # JUMP ?ret
                        0xE8, 0x3F, 0x12, 0x34, # PUSH 1234
                        0xAB, 0x00])            # ret: RET STACK
        self.assertEqual(self._run_code(), 0xFEDC) 
        
    def testJumpBack(self):
        self._set_code([0xE8, 0x3F, 0xFE, 0xDC, # PUSH FEDC
                        0x9C, 0x03,             # JUMP ?a
                        0xAB, 0x00,             # ret: RET STACK
                        0x8c, 0xFF, -3&0xFF])      # a: JUMP ?ret
        self.assertEqual(self._run_code(), 0xFEDC) 
        
    def testJZFalse(self):
        self._set_code([0x90, 0x01, 0xC3,       # JZ 01, ?a
                        0xB0,                   # RTRUE
                        0xB1])                  # a: RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJZTrue(self):
        self._set_code([0x90, 0x00, 0xC3,       # JZ 00, ?a
                        0xB0,                   # RTRUE
                        0xB1])                  # a: RFALSE
        self.assertEqual(self._run_code(), 0) 
        
    def testJNZFalse(self):
        self._set_code([0x90, 0x01, 0x43,       # JZ 01, ?~a
                        0xB0,                   # RTRUE
                        0xB1])                  # a: RFALSE
        self.assertEqual(self._run_code(), 0) 
        
    def testJNZTrue(self):
        self._set_code([0x90, 0x00, 0x43,       # JZ 00, ?~a
                        0xB0,                   # RTRUE
                        0xB1])                  # a: RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJZFalseLongBranch(self):
        self._set_code([0x90, 0x01, 0x80, 0x03, # JZ 01, ?a
                        0xB0,                   # RTRUE
                        0xB1])                  # a: RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJZTrueLongBranch(self):
        self._set_code([0x90, 0x00, 0x80, 0x03, # JZ 00, ?a
                        0xB0,                   # RTRUE
                        0xB1])                  # a: RFALSE
        self.assertEqual(self._run_code(), 0) 
        
    def testJZBackwards(self):
        self._set_code([0x90, 0x00, 0x80, 0x03, # JZ 00, ?a
                        0xB0,                   # b: RTRUE
                        0x90, 0x00, 0xBF, 0xFD, # a: JZ 00 ?b
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1)         
        
    def testJZRFalse(self):
        self._set_code([0x90, 0x00, 0xC0,       # JZ 00, RFALSE
                        0xB0])                  # RTRUE
        self.assertEqual(self._run_code(), 0) 
        
    def testJZRTrue(self):
        self._set_code([0x90, 0x00, 0xC1,       # JZ 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJERtrue(self):
        self._set_code([0x01, 0x01, 0x01, 0xC1, # JE 01, 01, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJERfalse(self):
        self._set_code([0x01, 0x01, 0x02, 0xC1, # JE 01, 02, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        
    def testJEAnyRfalse(self):
        self._set_code([0xC1, 0x55, 0x01, 0x02, 0x03, 0x04, 0xC1, # JE 01, 02, 03, 04, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        
    def testJEAnyRtrue(self):
        self._set_code([0xC1, 0x55, 0x01, 0x02, 0x03, 0x01, 0xC1, # JE 01, 02, 03, 01, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJENAnyRfalse(self):
        self._set_code([0xC1, 0x55, 0x01, 0x02, 0x03, 0x04, 0x41, # JE 01, 02, 03, 04, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJLRtrue(self):
        self._set_code([0x02, 0x01, 0x02, 0xC1, # JL 01, 02, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJLRfalse(self):
        self._set_code([0x02, 0x01, 0x01, 0xC1, # JL 01, 01, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
       
    def testJLSigned(self):
        self.memory.set_global(0, 0xFFFF)
        self._set_code([0x42, 0x10, 0x00, 0xC1, # JL G00, 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
       
    def testJGRtrue(self):
        self._set_code([0x03, 0x02, 0x01, 0xC1, # JG 02, 01, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testJGRfalse(self):
        self._set_code([0x03, 0x01, 0x01, 0xC1, # JG 01, 01, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
       
    def testJGSigned(self):
        self.memory.set_global(0, 0xFFFF)
        self._set_code([0x43, 0x10, 0x00, 0xC1, # JL G00, 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
       
    def testDecChkYes(self):
        self.memory.set_global(0, 0)
        self._set_code([0x04, 0x10, 0x00, 0xC1, # DEC_CHK G00, 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        self.assertEqual(self.memory.get_global(0), 0xFFFF)
        
    def testDecChkNo(self):
        self.memory.set_global(0, 1)
        self._set_code([0x04, 0x10, 0x00, 0xC1, # DEC_CHK G00, 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        self.assertEqual(self.memory.get_global(0), 0)
        
    def testIncChkYes(self):
        self.memory.set_global(0, 0)
        self._set_code([0x05, 0x10, 0x00, 0xC1, # INC_CHK G00, 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        self.assertEqual(self.memory.get_global(0), 1)
        
    def testIncChkNo(self):
        self.memory.set_global(0, 0xFFFF)
        self._set_code([0x05, 0x10, 0x00, 0xC1, # INC_CHK G00, 00, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        self.assertEqual(self.memory.get_global(0), 0)
        
    def testTestYes(self):
        self.memory.set_global(0, 0x31)
        self._set_code([0x05, 0x10, 0x30, 0xC1, # TEST G00, 30, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testTestNo(self):
        self.memory.set_global(0, 0x11)
        self._set_code([0x05, 0x10, 0x30, 0xC1, # TEST G00, 30, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        
    def testVerify(self):
        self.interpreter.verify()
        self.assertEqual(self.interpreter.condition, True) 
        self._set_code([0x00])
        self.interpreter.verify()
        self.assertEqual(self.interpreter.condition, False) 
        
    def textZsciiToZstr(self):
        self.assertEquals(pyz.zsciitozstr(b'zorkmid'), bytes([0x7E, 0x97, 0x42, 0x4E, 0xA4, 0xA5]))
        self.assertEqual(pyz.zsciitozstr(b'cretin'), bytes([0x22, 0xEA, 0xE5, 0xD3]))
        self.assertEqual(pyz.zsciitozstr(b'Coal Mine'), bytes([0x11, 0x14, 0x1A, 0x20, 0x12, 0x4E, 0xCD, 0x45]))
        
    def testStrToZstr(self):
        self.assertEqual(pyz.strtozstr("zorkmid"), bytes([0x7E, 0x97, 0x42, 0x4E, 0xA4, 0xA5]))
        self.assertEqual(pyz.strtozstr("cretin"), bytes([0x22, 0xEA, 0xE5, 0xD3]))
        self.assertEqual(pyz.strtozstr("Coal Mine"), bytes([0x11, 0x14, 0x1A, 0x20, 0x12, 0x4E, 0xCD, 0x45]))
        
    def testZstrToZchars(self):
        self.assertEqual(self.memory.zcharlist_to_str((self.memory.zstrtozchars([0x7E, 0x97, 0x42, 0x4E, 0xA4, 0xA5]))), "zorkmid")
        self.assertEqual(self.memory.zcharlist_to_str(self.memory.zstrtozchars([0x22, 0xEA, 0xE5, 0xD3])), "cretin")
        self.assertEqual(self.memory.zcharlist_to_str(self.memory.zstrtozchars([0x11, 0x14, 0x1A, 0x20, 0x12, 0x4E, 0xCD, 0x45])), "Coal Mine")
        
    def testZstrToZcharsAbbreviations(self):
        zork_str = [0x13, 0xE4, 0x50, 0x97, 0x12, 0x00, 0x04, 
                    0xA6, 0x02, 0xEA, 0x31, 0xD8, 0x65, 0x57, 0x29,  
                    0x20, 0x66, 0xE6, 0x25, 0x52, 0x1A, 0xF0, 0x00,  
                    0x2A, 0x11, 0xD3, 0x2E, 0x88, 0x52, 0x41, 0x0C,  
                    0x8E, 0x4D, 0x05, 0x48, 0xA7, 0x12, 0xEA, 0x6D,  
                    0xD8, 0xB8, 0x52]
        self.assertEqual(self.memory.zcharlist_to_str(self.memory.zstrtozchars(zork_str)),
                         "ZORK is a registered trademark of Infocom, Inc.\nRevision ")
        
    def testZstrToZcharsAlphabet2Code6(self):
        self.assertEqual(self.memory.zcharlist_to_str(self.memory.zstrtozchars([0x14, 0xC1, 0xF8, 0xA5])), ">")

        
    def testTestAttr(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x0A, 0x00, 0x00, 0xC1, # TEST_ATTR 0, 0, RTRUE
                            0xB1])                  # RFALSE
            self._run_code()
            
        self._set_code([0x0A, 0x01, 0x00, 0xC1, # TEST_ATTR 1, 0, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        self._set_code([0x0A, 0x01, 0x10, 0xC1, # TEST_ATTR 1, 10, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        self._set_code([0x0A, 0x02, 0x00, 0xC1, # TEST_ATTR 2, 0, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0) 
        self._set_code([0x0A, 0x02, 0x10, 0xC1, # TEST_ATTR 2, 10, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1) 
        
    def testSetAttr(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x0B, 0x00, 0x10,       # SET_ATTR 0, 10
                            0x0A, 0x00, 0x10, 0xC1, # TEST_ATTR 0, 10, RTRUE
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x0B, 0x01, 0x10,       # SET_ATTR 1, 10
                        0x0A, 0x01, 0x10, 0xC1, # TEST_ATTR 1, 10, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1)
            
    def testClearAttr(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x0C, 0x00, 0x00,       # CLEAR_ATTR 0, 0
                            0x0A, 0x00, 0x00, 0xC1, # TEST_ATTR 0, 0, RTRUE
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x0C, 0x01, 0x00,       # CLEAR_ATTR 1, 0
                        0x0A, 0x01, 0x00, 0xC1, # TEST_ATTR 1, 0, RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0)
            
    def testGetPropAddr(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x12, 0x00, 0x00, 0x10, # GET_PROP_ADDR 0, 0 -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x12, 0x01, 0x0A, 0x10, # GET_PROP_ADDR 1, 0A -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0)
        self._set_code([0x12, 0x01, 0x11, 0x10, # GET_PROP_ADDR 1, 11 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), self.segment_offsets[8] + 4)
        
    def testGetProp(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x11, 0x00, 0x00, 0x10, # GET_PROP 0, 0 -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        with self.assertRaises(RuntimeError):
            self._set_code([0x11, 0x02, 0x12, 0x10, # GET_PROP 2, 12 -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x11, 0x01, 0x0A, 0x10, # GET_PROP 1, 0A -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0xCCDD)
        self._set_code([0x11, 0x02, 0x11, 0x10, # GET_PROP 2, 11 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x295C)
        
    def testGetNextProp(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x13, 0x00, 0x00, 0x10, # GET_NEXT_PROP 0, 0 -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        with self.assertRaises(RuntimeError):
            self._set_code([0x13, 0x01, 0x0A, 0x10, # GET_NEXT_PROP 1, 0A -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x13, 0x01, 0x00, 0x10, # GET_NEXT_PROP 1, 00 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x12)
        self._set_code([0x13, 0x01, 0x12, 0x10, # GET_NEXT_PROP 1, 12 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x11)
        self._set_code([0x13, 0x01, 0x11, 0x10, # GET_NEXT_PROP 1, 11 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x07)
        self._set_code([0x13, 0x01, 0x07, 0x10, # GET_NEXT_PROP 1, 07 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 0x00)

    def testGetPropLen(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x12, 0x02, 0x0A, 0x10, # GET_PROP_ADDR 2, 0A -> G00
                            0xA4, 0x10, 0x10,       # GET_PROP_LEN G00 -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x12, 0x02, 0x12, 0x10, # GET_PROP_ADDR 2, 12 -> G00
                        0xA4, 0x10, 0x10,       # GET_PROP_LEN G00 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 8)

    def testGetParent(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x93, 0x00, 0x10,       # GET_PARENT 0 -> G00
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x93, 0x02, 0x10,       # GET_PARENT 2 -> G00
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_global(0), 1)

    def testGetChild(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x92, 0x00, 0x10, 0xC1, # GET_CHILD 0 -> G00 ?RTRUE
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x92, 0x01, 0x10, 0xC1, # GET_CHILD 1 -> G00 ?RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 1)
        self.assertEqual(self.memory.get_global(0), 2)
        self._set_code([0x92, 0x02, 0x10, 0xC1, # GET_CHILD 2 -> G00 ?RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0)

    def testGetSibling(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0x91, 0x00, 0x10, 0xC1, # GET_SIBLING 0 -> G00 ?RTRUE
                            0xB1])                  # RFALSE
            self._run_code()
        self._set_code([0x91, 0x01, 0x10, 0xC1, # GET_SIBLING 1 -> G00 ?RTRUE
                        0xB1])                  # RFALSE
        self.assertEqual(self._run_code(), 0)
        
    def testPutProp(self):
        with self.assertRaises(RuntimeError):
            self._set_code([0xE3, 0x53, 0x03, 0x10, 0xAA, 0xBB, # PUT_PROP 3, 10, AABB 
                            0xB1])                              # RFALSE
            self._run_code()
        self._set_code([0xE3, 0x53, 0x03, 0x1E, 0xAA, 0xBB, # PUT_PROP 3, 1E, AABB 
                        0xB1])                              # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_object_prop_data(3, 30), bytearray([0xBB]))
        self._set_code([0xE3, 0x57, 0x01, 0x12, 0xAA,       # PUT_PROP 3, 1E, AA 
                        0xB1])                              # RFALSE
        self._run_code()
        self.assertEqual(self.memory.get_object_prop_data(1, 18), bytearray([0x00,0xAA]))
        
    def testInsertObject(self):
        # Make sure this is the starting state
        self.assertEquals(self.memory.get_object_parent(1), 0)
        self.assertEquals(self.memory.get_object_parent(2), 1)
        self.assertEquals(self.memory.get_object_parent(3), 0)
        self.assertEquals(self.memory.get_object_child(1), 2)
        self.assertEquals(self.memory.get_object_child(2), 0)
        self.assertEquals(self.memory.get_object_child(3), 0)
        self.assertEquals(self.memory.get_object_sibling(1), 0)
        self.assertEquals(self.memory.get_object_sibling(2), 0)
        self.assertEquals(self.memory.get_object_sibling(3), 0)
        
        self._set_code([0x0E, 0x03, 0x01,       # INSERT_OBJ 3, 1 
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEquals(self.memory.get_object_parent(1), 0)
        self.assertEquals(self.memory.get_object_parent(2), 1)
        self.assertEquals(self.memory.get_object_parent(3), 1)
        self.assertEquals(self.memory.get_object_child(1), 3)
        self.assertEquals(self.memory.get_object_child(2), 0)
        self.assertEquals(self.memory.get_object_child(3), 0)
        self.assertEquals(self.memory.get_object_sibling(1), 0)
        self.assertEquals(self.memory.get_object_sibling(2), 0)
        self.assertEquals(self.memory.get_object_sibling(3), 2)
        
        self._set_code([0x0E, 0x03, 0x02,       # INSERT_OBJ 3, 2 
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEquals(self.memory.get_object_parent(1), 0)
        self.assertEquals(self.memory.get_object_parent(2), 1)
        self.assertEquals(self.memory.get_object_parent(3), 2)
        self.assertEquals(self.memory.get_object_child(1), 2)
        self.assertEquals(self.memory.get_object_child(2), 3)
        self.assertEquals(self.memory.get_object_child(3), 0)
        self.assertEquals(self.memory.get_object_sibling(1), 0)
        self.assertEquals(self.memory.get_object_sibling(2), 0)
        self.assertEquals(self.memory.get_object_sibling(3), 0)

        self._set_code([0x0E, 0x03, 0x01,       # INSERT_OBJ 3, 1 
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEquals(self.memory.get_object_parent(1), 0)
        self.assertEquals(self.memory.get_object_parent(2), 1)
        self.assertEquals(self.memory.get_object_parent(3), 1)
        self.assertEquals(self.memory.get_object_child(1), 3)
        self.assertEquals(self.memory.get_object_child(2), 0)
        self.assertEquals(self.memory.get_object_child(3), 0)
        self.assertEquals(self.memory.get_object_sibling(1), 0)
        self.assertEquals(self.memory.get_object_sibling(2), 0)
        self.assertEquals(self.memory.get_object_sibling(3), 2)

        self._set_code([0x0E, 0x02, 0x03,       # INSERT_OBJ 2, 3 
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEquals(self.memory.get_object_parent(1), 0)
        self.assertEquals(self.memory.get_object_parent(2), 3)
        self.assertEquals(self.memory.get_object_parent(3), 1)
        self.assertEquals(self.memory.get_object_child(1), 3)
        self.assertEquals(self.memory.get_object_child(2), 0)
        self.assertEquals(self.memory.get_object_child(3), 2)
        self.assertEquals(self.memory.get_object_sibling(1), 0)
        self.assertEquals(self.memory.get_object_sibling(2), 0)
        self.assertEquals(self.memory.get_object_sibling(3), 0)
        
    def testJin(self):
        self._set_code([0x06, 0x02, 0x01, 0xC1, # JIN 2, 1 ?RTRUE 
                        0xB1])                  # RFALSE
        self.assertEquals(self._run_code(), 1)
        self._set_code([0x06, 0x01, 0x02, 0xC1, # JIN 2, 1 ?RTRUE 
                        0xB1])                  # RFALSE
        self.assertEquals(self._run_code(), 0)
        
    def testRemoveObj(self):
        self._set_code([0x99, 0x02,             # REMOVE_OBJ 2 
                        0xB1])                  # RFALSE
        self._run_code()
        self.assertEquals(self.memory.get_object_parent(1), 0)
        self.assertEquals(self.memory.get_object_parent(2), 0)
        self.assertEquals(self.memory.get_object_parent(3), 0)
        self.assertEquals(self.memory.get_object_child(1), 0)
        self.assertEquals(self.memory.get_object_child(2), 0)
        self.assertEquals(self.memory.get_object_child(3), 0)
        self.assertEquals(self.memory.get_object_sibling(1), 0)
        self.assertEquals(self.memory.get_object_sibling(2), 0)
        self.assertEquals(self.memory.get_object_sibling(3), 0)
        
    def testCall(self):
        self._set_code([0xE0, 0x3F, 0x00, 0x00, 0x10,   # CALL r -> G00
                        0xB1,                           # RFALSE
                        0x00,                           # r: 0 locals
                        0xB0])                          # RTRUE
        self._fixup_code_for_packed_location(2, 6)
        self.assertEquals(self._run_code(), 0)
        self.assertEquals(self.memory.get_global(0), 1)
        
        self._set_code([0xE0, 0x3F, 0x00, 0x00, 0x10,   # CALL r -> G00
                        0xB1,                           # RFALSE
                        0x01, 0xFF, 0xAA,               # r: 1 local, L00 = FFAA
                        0xAB, 0x01])                    # RET L00
        self._fixup_code_for_packed_location(2, 6)
        self.assertEquals(self._run_code(), 0)
        self.assertEquals(self.memory.get_global(0), 0xFFAA)
        
    def testCall0(self):
        self._set_code([0xE0, 0x3F, 0x00, 0x00, 0x10,   # CALL 0000 -> G00
                        0xB0])                          # TRUE
        self.assertEquals(self._run_code(), 1)
        self.assertEquals(self.memory.get_global(0), 0)
        
    def testCallImmediateArg(self):
        self._set_code([0xE0, 0x0F, 0x00, 0x00, 0xAB, 0xCD, 0x10,   # CALL r, 0xABCD -> G00
                        0xB1,                           # RFALSE
                        0x01, 0xFF, 0xAA,               # r: 1 local, L00 = FFAA
                        0xAB, 0x01])                    # RET L00
        self._fixup_code_for_packed_location(2, 8)
        self.assertEquals(self._run_code(), 0)
        self.assertEquals(self.memory.get_global(0), 0xABCD)
        
    def testCallVarArg(self):
        self._set_code([0xE0, 0x2F, 0x00, 0x00, 0x10, 0x10,   # CALL r, G00 -> G00
                        0xB1,                           # RFALSE
                        0x00,                           # padding
                        0x01, 0xFF, 0xAA,               # r: 1 local, L00 = FFAA
                        0xAB, 0x01])                    # RET L00
        self._fixup_code_for_packed_location(2, 8)
        self.memory.set_global(0, 0xABCD)
        self.assertEquals(self._run_code(), 0)
        self.assertEquals(self.memory.get_global(0), 0xABCD)
        
    def testCallStackArg(self):
        self._set_code([0xE8, 0x3F, 0x00, 0x00,         # PUSH 0000
                        0xE0, 0x2F, 0x00, 0x00, 0x10, 0x10,   # CALL r, (stack) -> (stack)
                        0xB8,                           # RET_POPPED
                        0x00,                           # padding
                        0x01, 0xFF, 0xAA,               # r: 1 local, L00 = FFAA
                        0xAB, 0x01])                    # RET L00
        self._fixup_code_for_packed_location(6, 12)
        self.assertEquals(self._run_code(), 0x0000)

    def testCallIndirect(self):
        self._set_code([0xE8, 0x3F, 0x00, 0x00,         # PUSH r
                        0xE0, 0xBF, 0x00, 0x10,         # CALL (STACK) -> G00
                        0xB1,                           # RFALSE
                        0x00,                           # padding
                        0x00,                           # r: 0 locals
                        0xB0])                          # RTRUE
        self._fixup_code_for_packed_location(2, 10)
        self.assertEquals(self._run_code(), 0)
        self.assertEquals(self.memory.get_global(0), 1)
        
        
    def testPrintChar(self):
        self._set_code([0xE5, 0x7F, ord('x'),           # PRINT_CHAR 'x'
                        0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), 'x')
        self._set_code([0xE5, 0x7F, 0x9B,               # PRINT_CHAR 9B (unicode 00E4)
                        0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), 'x\u00E4')

    def testPrintAddr(self):
        zstr = list(pyz.strtozstr("zorkmid"))
        self._set_code([0x87, 0x00, 0x00,               # PRINT_ADDR s
                        0xB1] +                         # RFALSE
                       zstr)                            # s: "zorkmid"
        self._fixup_code_for_location(1, 4)
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "zorkmid")

    def testPrintPAddr(self):
        zstr = list(pyz.strtozstr("zorkmid"))
        self._set_code([0x8D, 0x00, 0x00,               # PRINT_PADDR s
                        0xB1] +                         # RFALSE
                       zstr)                            # s: "zorkmid"
        self._fixup_code_for_packed_location(1, 4)
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "zorkmid")

    def testNewline(self):
        self._set_code([0xBB,                           # NEW_LINE
                        0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "\n")
        
    def testPrintNum(self):
        self._set_code([0xE6, 0x7F, 0xF0,               # PRINT_NUM F0
                        0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "240")
        self._set_code([0xE6, 0x3F, 0xF0, 0x00,         # PRINT_NUM F000
                        0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "240-4096")
     
    def testPrintObj(self):
        self._set_code([0x9A, 0x01,                     # PRINT_OBJ 01
                        0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "thing1")

    def testPrint(self):
        zstr = list(pyz.strtozstr("zorkmid"))
        self._set_code([0xB2] +                         # PRINT "zorkmid"
                       zstr +
                       [0xB1])                          # RFALSE
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "zorkmid")
        
    def testPrintRet(self):
        zstr = list(pyz.strtozstr("zorkmid"))
        self._set_code([0xB3] +                         # PRINT_RET "zorkmid"
                       zstr)
        self.assertEquals(self._run_code(), 1)
        self.assertEquals(self.ostr.getvalue(), "zorkmid\n")
        
    def testShowStatus(self):
        self._set_code([0xBC,                           # SHOW_STATUS
                        0xB1])                          # RFALSE
        self.memory.set_global(0, 1)
        self.memory.set_global(1, -9 & 0xFFFF)
        self.memory.set_global(2, 230)
        self._run_code()
        self.assertEquals(self.ostr.getvalue(), "thing1 -9 / 230\n")
        
    def testSreadTextBuffer(self):
        bufferloc = self.memory.header.initial_PC
        self._set_code([0x10] + [0]*100)                # maxchars = 10
        
        self.istr.seek(0)
        print("123ABC\u7878def\n", end='', file=self.istr)
        self.istr.seek(0)
        self.memory.sread_readline_to_text_buffer(bufferloc)
        self.assertEquals(self.memory.memory[bufferloc : bufferloc + 12],
                          bytearray([0x10, 0x31, 0x32, 0x33, 97, 98, 99, 100, 101, 102, 0, 0]))
        
        self.istr.seek(0)
        print("12345678901234567890\n", end='', file=self.istr)
        self.istr.seek(0)
        self.memory.sread_readline_to_text_buffer(bufferloc)
        self.assertEquals(self.memory.memory[bufferloc : bufferloc + 17],
                          bytearray([0x10, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
                                     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0]))
        
        self.istr.seek(0)
        print("123ABC\u7878def\n", end='', file=self.istr)
        self.istr.seek(0)
        self.memory.sread_readline_to_text_buffer(bufferloc)
        self.assertEquals(self.memory.memory[bufferloc : bufferloc + 11],
                          bytearray([0x10, 0x31, 0x32, 0x33, 97, 98, 99, 100, 101, 102, 0]))
        
    def testSreadSplit(self):
        bufferloc = self.memory.header.initial_PC
        self._set_code([0x20] + [0]*100)
        self.istr.seek(0)
        print(" fred,go  fishing \n", end='', file=self.istr)
        self.istr.seek(0)
        self.memory.sread_readline_to_text_buffer(bufferloc)
        words = self.memory.split_with_separators(bufferloc, b'.,"')
        self.assertEquals(words, [(2,b'fred'), (6,b','), (7,b'go'), (11,b'fishing')])
        
    def testSreadParse(self):
        buffer_loc = self.memory.header.initial_PC
        dictionary = [0x03] + list(b',."') + [0x05, 0x00, 0x04]
        dictionary += list(pyz.zsciitozstr(b'fishing', 2)) + [0xFF]
        dictionary += list(pyz.zsciitozstr(b'fred', 2)) + [0xFF]
        dictionary += list(pyz.zsciitozstr(b'fuzzycat', 2)) + [0xFF]
        dictionary += list(pyz.zsciitozstr(b'go', 2)) + [0xFF]
        self._set_code([0x20] + [0]*0x2F + [0x20] + [0]*0x2F + dictionary)
        parse_buffer_loc = buffer_loc + 0x30
        dictionary_loc = buffer_loc + 0x60
        self.istr.seek(0)
        print(" fred,go  fishing \n", end='', file=self.istr)
        self.istr.seek(0)
        self.memory.sread_readline_to_text_buffer(buffer_loc)
        self.memory.parse_by_dictionary(buffer_loc, parse_buffer_loc, dictionary_loc)
        word_locs = list(map(lambda x: dictionary_loc + 7 + 5*x, range(0, 4)))
        self.assertEquals(list(self.memory.memory[parse_buffer_loc : parse_buffer_loc + 0x30]),
                          [0x20, 0x04, 
                           word_locs[1]>>8, word_locs[1]&0xFF, 0x04, 0x02,
                           0x00, 0x00, 0x01, 0x06,
                           word_locs[3]>>8, word_locs[3]&0xFF, 0x02, 0x07,
                           word_locs[0]>>8, word_locs[0]&0xFF, 0x07, 0x0B] + [0x00]*(48-18))
        

if __name__ == "__main__":
    suite = unittest.TestLoader().loadTestsFromTestCase(Test)
    unittest.TextTestRunner(verbosity=2).run(suite)